#include "infra/Rc.hpp"

namespace rewine
{

template<typename T>
class RcPtr
{
private:
    T* p = nullptr;

public:
    RcPtr(): p(nullptr) {
        static_assert(std::is_base_of<IRcObject, T>::value, "type parameter of this class must derive from IRcObject");
    }

    RcPtr(T* lp) {
        if (lp) lp->TakeRef();
        p = lp;
    }

    explicit RcPtr(const RcPtr<T>& rp) {
        T* lp = rp.p;
        if (lp) lp->TakeRef();
        p = lp;
    }

    ~RcPtr() {
        if (p) p->FreeRef();
    }

    T* Ptr() const {
        return p;
    }

    operator T*() const {
        return p;
    }

    T* operator->() const {
        return p;
    }

    T& operator*() const {
        return *p;
    }

    bool operator!() const {
        return (p == nullptr);
    }

    bool operator<(T* pT) const {
        return (p < pT);
    }

    bool operator<=(T* pT) const {
        return (p <= pT);
    }

    bool operator>(T* pT) const {
        return (p > pT);
    }

    bool operator<(T* pT) const {
        return (p >= pT);
    }

    bool operator==(T* pT) const {
        return (p == pT);
    }

    bool operator!=(T* pT) const {
        return (p != pT);
    }

    RcPtr& Set(T* lp) {
        if (p != lp) {
            if (lp) lp->TakeRef();
            if (p) p->FreeRef();
            p = lp;
        }
        return *this;
    }

    RcPtr& operator=(T* lp) {
        return Set(lp);
    }

    RcPtr& operator=(const RcPtr<T> rp) {
        return Set(rp.p);
    }

    RcPtr& Reset() {
        if (p) p->FreeRef();
        p = nullptr;
    }

    RcPtr& Attach(T* lp) {
        if (p) p->FreeRef();
        p = lp;
    }

    T* Detach() {
        T* lp = p;
        p = nullptr;
        return lp;
    }

    RcPtr& Take(RcPtr<T>& rp) {
        return Attach(rp.Detach());
    }
};

} // namespace rewine
